;(function($) {
	var helper = {
		isInputControl: function($item) {
			var allowType = ['text', 'color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'number', 'password', 'range', 'search', 'time', 'url', 'week'];
			var tagName = $item.prop('tagName');
			var type = $item.attr('type');
			var isInput = (tagName === 'INPUT') && ($.inArray(type, allowType) > -1 || type === '' || type === null);
			var isTextarea = (tagName === 'TEXTAREA');
			return isInput || isTextarea;
		},
		isChecboxControl: function($item) {
			var tagName = $item.prop('tagName');
			var type = $item.attr('type');
			return tagName === 'INPUT' && type === 'checkbox';
		},
		isRadioControl: function($item) {
			var tagName = $item.prop('tagName');
			var type = $item.attr('type');
			return tagName === 'INPUT' && type === 'radio';
		},
		isSelectControl: function($item) {
			var tagName = $item.prop('tagName');
			return tagName === 'SELECT';
		},
		isRegExp: function(param) {
			return Object.prototype.toString.call(param) === '[object RegExp]';
		},
		isFloat: function(param) {
			return /^[-]?[0-9]*\.?[0-9]+$/.test(param);
		},
		isInt: function(param) {
			return /^[-]?[0-9]+$/.test(param);
		}
	};

	var defaultsMethod = {
		notEmpty: {//不为空，不能为空字符串
			func: function(param, $item) {
				if (!helper.isInputControl($item)) {
					return true;//不同方法针对不同控件才有效
				}
			//param是rules里边写的值，value是控件上的值。对于notEmpty，param一般是true，表示要非空。
			//false则是不需要，返回true
				if (param) {
					return $.trim($item.val()) !== '';
				} else {
					return true;
				}
				
			},
			msg: '不能留空或纯空格',
		},
		maxLength: {//字符串最大长度
			func: function(param, $item) {
				if (!helper.isInputControl($item)) {
					return true;
				}
				return $item.val().length <= parseInt(param);
			},
			msg: '长度不要超过[param]个字符',
		},
		minLength: {//字符串最小长度
			func: function(param, $item) {
				if (!helper.isInputControl($item)) {
					return true;//不同方法针对不同控件才有效
				}
				return $item.val().length >= parseInt(param);
			},
			msg: '长度不要少于[param]个字符',
		},
		float: {//是否浮点数，包含正负
			func: function(param, $item) {
				if (!helper.isInputControl($item)) {
					return true;
				}
				if (param) {
					var value = $.trim($item.val());
					if (helper.isFloat(value)) {
						return true;
					} else {
						return false;
					}
				} else {
					return true;
				}
			},
			msg: '只能是浮点数',
		},
		integer: {//是否整数，含正负
			func: function(param, $item) {
				if (param) {
					if (!helper.isInputControl($item)) {
						return true;
					}
					var value = $.trim($item.val());
					if (helper.isInt(value)) {
						return true;
					} else {
						return false;
					}
				} else {
					return true;
				}
			},
			msg: '只能是整数',
		},
		max: {//数值最大值
			func: function(param, $item) {
				if (helper.isInputControl($item) || helper.isSelectControl($item)) {
					var value = $.trim($item.val());
					if (!helper.isFloat(param)) {
						return true;//给定的参数都不是浮点数，直接返回true，规则无效，验证通过
					}
					if (!helper.isFloat(value)) {
						return false;//不是浮点数，验证失败
					}
					return parseFloat(value) <= parseFloat(param);
				}
				return true;
			},
			msg: '最大不要大于[param]',
		},
		min: {//数值最小值
			func: function(param, $item) {
				if (helper.isInputControl($item) || helper.isSelectControl($item)) {
					var value = $.trim($item.val());
					if (!helper.isFloat( param)) {
						return true;//给定的参数都不是浮点数，直接返回true，规则无效，验证通过
					}
					if (!helper.isFloat(value)) {
						return false;//不是浮点数，验证失败
					}
					return parseFloat(value) >= parseFloat(param);
				}
				return true;
			},
			msg: '最小不要小于[param]',
		},
		range: {//数值范围
			func: function(param, $item) {
				if (helper.isInputControl($item) || helper.isSelectControl($item)) {
					if (!$.isArray(param) || param.length != 2) {
						return true;
					}
					if (!helper.isFloat(param[0]) || !helper.isFloat(param[1])) {
						return true;//给定的参数都不是浮点数，直接返回true，规则无效，验证通过
					}
					var value = parseFloat($.trim($item.val()));
					if (typeof value !== 'number') {
						return false;
					}
					return value >= param[0] && value <= param[1];
				}
				return true;
			},
			msg: '数值有效范围[param]',
		},
		checked: {//针对checkbox&radio，必须选择至少一项。
			func: function(param, $item, itemObj) {
				if (param === true) {
					if (helper.isChecboxControl($item)) {		
						return $item.closest('form').find(itemObj.selector+':checked').length > 0;						
					} else if (helper.isRadioControl($item)) {
						return $item.closest('form').find(itemObj.selector+':checked').length > 0;	
					}
				}
				return true;
			},
			msg: '至少选择一项',
		},
		checkedNumberMin: {//至少选择多少项
			func: function(param, $item, itemObj) {
				if (helper.isInt(param) && param >= 1) {
					var name = $item.attr('name');
					if (helper.isChecboxControl($item)) {
						return $item.closest('form').find(itemObj.selector+':checked').length >= parseInt(param);	
					}
				}
				return true;
			},
			msg: '至少选择[param]项',
		},
		checkedNumberMax: {//至多选择多少项
			func: function(param, $item, itemObj) {
				if (helper.isInt(param) && param >= 1) {
					var name = $item.attr('name');
					if (helper.isChecboxControl($item)) {
						return $item.closest('form').find(itemObj.selector+':checked').length <= parseInt(param);	
					}
				}
				return true;
			},
			msg: '至多选择[param]项',
		},
		checkedInclude: {//至少得包含哪些项
			func: function(param, $item, itemObj) {
				if ($.isArray(param)) {
					var name = $item.attr('name');
					if (helper.isChecboxControl($item)) {
						var haved = $item.closest('form').find(itemObj.selector+':checked').map(function() {
							return $(this).val();
						}).get();
					} else {
						return true;
					}
					var result = true;
					$.each(param, function(index, item) {
						console.log('inArray:',item,haved);
						if ($.inArray(item+'', haved) == -1) {
							result = false;
							return false;
						}
					});
					return result;
				}
				return true;
			},
			msg: '选择的项中应该包含[param]',
		},
		selected: {//针对selected，必须选择至少一项
			func: function(param, $item) {
				if (param === true) {
					if (helper.isSelectControl($item)) {							
						return $item.find('option:selected').length > 0;					
					}
				}
				return true;
			},
			msg: '至少选择一项',
		},
		selectedNumberMin: {//至少选择多少项
			func: function(param, $item) {
				if (helper.isInt(param) && param >= 1) {
					if (helper.isSelectControl($item)) {
						return $item.find('option:selected').length >= parseInt(param);	
					}
				}
				return true;
			},
			msg: '至少选择[param]项',
		},
		selectedNumberMax: {//至少选择多少项
			func: function(param, $item) {
				if (helper.isInt(param) && param >= 1) {
					if (helper.isSelectControl($item)) {
						return $item.find('option:selected').length <= parseInt(param);	
					}
				}
				return true;
			},
			msg: '至多选择[param]项',
		},
		selectedInclude: {//至少得包含哪些项
			func: function(param, $item) {
				if ($.isArray(param)) {
					if (helper.isSelectControl($item)) {
						var haved = $item.find('option:selected').map(function() {
							return $(this).val();
						}).get();
					} else {
						return true;
					}
					var result = true;
					$.each(param, function(index, item) {
						if ($.inArray(item+'', haved) == -1) {
							result = false;
							return false;
						}
					});
					return result;
				}
				return true;
			},
			msg: '选择的项中应该包含[param]',
		},
		pregMatch: {
			func: function(param, $item) {
				if (helper.isRegExp(param) && helper.isInputControl($item)) {
					var value = $item.val();
					return param.test(value);
				}
				return true;
			},
			msg: '不能匹配表达式[param]',
		},
		equateTo: {
			func: function(param, $item) {
				if (helper.isInputControl($item) || helper.isSelectControl($item)) {
					var $target = $(param);
					if ($target.length) {
						return $target.first().val() === $item.val();
					}
				}
				return true;
			},
			msg: '必须跟[param]相等',
		}

	};

	var customMethod = {};//自定义规则
	var customClassRule = {};
	var customIdRule = {};
	var defaults = {
		errorClass: 'error',//错误时添加的类名，会在表单控件和提示标签上同时添加
		errorLabel: 'label',//提示信息的标签，默认label，一般还可以用span
		validateAll: false,
		//提交表单验证时遇到错误是否停止，默认false表示不验证全部即刻停止只显示一条错误
		//为true验证完所有控件显示全部错误
		validateBlur: true,//失去焦点时是否验证，默认ture
		validateInput: true,//输入时是否验证，默认true
		validateChange: true,//值改变时是否验证，默认true
		validateBeforeSubmit: false,//没进行过提交前是否进行验证。默认false，只有进行过一次提交上边的失焦与输入验证才进行
		fail: '',//验证不通过回调，参数是当前的验证控件与提示标签，均为jQuery对象
		success: '',//验证通过回调，参数是当前控件，jQuery对象
		rules: {},//验证规则
		messages: {},//对应验证规则的提示信息

	};

	$.extend({XValidate: {}});
	$.extend($.XValidate, {
		addMethod: function(name, func, msg) {
			if (!(name in customMethod) && (typeof func === 'function')) {
				customMethod[name] = {
					"func": func,
					"msg": msg,
				}
			}			
		},
		addClassRule: function(className, rules, messages) {
			if (!customClassRule[className]) {
				customClassRule[className] = {
					'rules': rules,
					'messages': messages,
					'custom': true,
				}
			}
		},
		addIdRule: function(id, rules, messages) {
			if (!customIdRule[id]) {
				customIdRule[id] = {
					'rules': rules,
					'messages': messages,
					'custom': true,
				}
			}
		},
	});

	
	var XValidate = function($form, options) {

		var self = this;
		this.form = $form;
		var options = this.options = $.extend({}, defaults, options);
		var rules = this.rules = options.rules || {};
		var messages = this.messages = options.messages || {};
		var $errorLabel = this.errorLabel = self.getErrorLabel();
		var inputList = {}, selectList = {}, checkboxList = {}, radioList = {};

		this.doSubmit = false;//是否提交过

		$form.attr('novalidate', 'novalidate');	

		var itemObjList = this.itemObjList = {};	
		

		//事件列表
		var eventList = '';
		if (options.validateBlur) {
			eventList += 'blur ';
		}
		if (options.validateInput) {
			eventList += 'input ';
		}
		if (options.validateChange) {
			eventList += 'change ';
		}
		eventList = $.trim(eventList);
		
		//通过name定位的元素
		var nameSelectors = '';
		for(var i in rules) {
			nameSelectors += '[name="'+i+'"],';
		}	
		nameSelectors = nameSelectors.substring(0, nameSelectors.length-1);

		//通过addRule添加的元素
		var classSelectors = '';
		for (var i in customClassRule) {
			classSelectors += i+',';
		}
		classSelectors = classSelectors.substring(0, classSelectors.length-1);

		//通过IdRule添加的元素
		var idSelectors = '';
		for (var i in customIdRule) {
			idSelectors += i+',';
		}
		idSelectors = idSelectors.substring(0, idSelectors.length-1);
		
		if (nameSelectors !== '') {
			$form.on(eventList, nameSelectors, function(e) {
				if (!self.options.validateBeforeSubmit && !self.doSubmit) {
					return;
				}
				self.handleEvent(this, 'name', e);
			});
		}
		
		if (classSelectors !== '') {
			$form.on(eventList, classSelectors, function(e) {
				if (!self.options.validateBeforeSubmit && !self.doSubmit) {
					return;
				}
				self.handleEvent(this, 'class', e);	
			});
		}
		
		if (idSelectors !== '') {
			$form.on(eventList, idSelectors, function(e) {
				if (!self.options.validateBeforeSubmit && !self.doSubmit) {
					return;
				}			
				self.handleEvent(this, 'id', e);
			});
		}	
		

		//提交事件		
		$form.on('submit', function(e) {
			self.doSubmit = true;
			var stop = false;
			$(nameSelectors).each(function(index, value) {
				var result = self.handleEvent(this, 'name', e);
				if (!result && !self.options.validateAll) {
					stop = true;
					return false;
				}
			});
			if (stop) {
				return false;
			}

			$(classSelectors).each(function(index, value) {
				var result = self.handleEvent(this, 'class', e);
				if (!result && !self.options.validateAll) {
					stop = true;
				}
			});
			if (stop) {
				return false;
			}
			
			$(idSelectors).each(function(index, value) {
				var result = self.handleEvent(this, 'id', e);
				if (!result && !self.options.validateAll) {
					stop = true;
				}
			});
			if (stop) {
				return false;
			}

			
			if (self.options.submitHandler && $.isFunction(self.options.submitHandler)) {
				var doSubmit = self.options.submitHandler.call(this, $(this));
				console.log('doSubmit:'+doSubmit);
				if (doSubmit === false) {
					return false;
				}
			}
		});
		
	};
	

	XValidate.prototype = {
		constructor: XValidate,			
		handleEvent: function(item, type, e) {
			e.stopPropagation();
			if (e.type !== 'submit') {
				e.preventDefault();//阻止表单无法提交
			}
			
			var $item = $(item);
			if ((e.type === 'input' || e.type === 'focusout') && (helper.isSelectControl($item) || helper.isChecboxControl($item))) {
				console.log(e.type+' return');
				return;
			}
			var name, selector;
			if (type === 'name') {
				name = item.name;
				selector = '[name="'+name+'"]';
			} else if (type === 'class') {
				name = selector = this.getClassName(item.className);
			} else if (type === 'id') {
				name = selector = '#'+item.id;
			}
			var itemObj;
			if (this.itemObjList[name]) {
				itemObj = this.itemObjList[name];
			} else {
				itemObj = {
					error_method: null,
					field: name,
					elem: item,
					selector: selector,
				};
				this.itemObjList[name] = itemObj;
			}
			if (type === 'name') {
				itemObj.elems = this.form.find(selector);
			} else {
				itemObj.elems = $item;
			}
			
			var error = this.validateItem(itemObj, name);
			console.log("errorObj:",error);					
			if (!$.isEmptyObject(error)) {
				this.fail(itemObj, error);
				return false;
			} else {
				this.success(itemObj);
				return true;
			}
		},
		fail: function(itemObj, error) {				
			// if (itemObj.error_method === error.method) {
			// 	return;
			// }
			// if (!itemObj.valid) {
				this.success(itemObj);//先调用success清空一个错误信息
			// }
			itemObj.error_method = error.method;
			var $item = itemObj.elems;
			console.log('fail',itemObj);
			var errorClass = 'xvalidate-error-'+error.field;
			var $errorLabel = this.errorLabel.clone().addClass(errorClass).text(error.errorText);
			if (typeof this.options.fail === 'function' && typeof this.options.success === 'function') {
				this.options.fail.call($item.get(0), $item, $errorLabel, error);
			} else {					
				$item.addClass(this.options.errorClass).after($errorLabel);			
			}
		},
		success: function(itemObj) {				
			// if (itemObj.valid) {
			// 	return;
			// }
			itemObj.error_method = null;
			var $item = itemObj.elems;		
			if (typeof this.options.fail === 'function' && typeof this.options.success === 'function') {
				this.options.success.call($item.get(0), $item);
			} else {
				var $preErrorLabel = $item.next();
				if ($preErrorLabel.length && $preErrorLabel.hasClass(this.options.errorClass)) {
					$preErrorLabel.remove();
				}
			}
		},
		getErrorLabel: function() {			
			//默认的错误是在当前控制后边添加一个label。				
			var label = this.options.errorLabel;
			if (typeof label === 'string' && $.trim(label !== '')) {
				result = label.replace(/<|\/|>|\s>/g, '');
			} else {
				result = 'label';
			}
			var $error = $('<'+result+'>');
			$error.addClass(this.options.errorClass);
			return $error;
		},
		shouldDoValidate: function(item, methods) {
			var result = true;
			if ('validate' in methods) {
				var _doValidate = methods['validate'];
				if (_doValidate === false || _doValidate === 0) {
					result = false;
				} else if (typeof _doValidate === 'function') {
					result = _doValidate.call(item, $(item));
				}
			}
			return !!result;
		},
		validateItem: function(itemObj, field) {
			console.log('validateItem:',field);
			// debugger;
			var error = {};		
			var methods = this.getMethodsByField(field);
			if (!methods) {
				console.log('找不到规则方法:'+field);
				return null;
			}
			var _this = this;
			$.each(itemObj.elems, function(key, value) {
				var shouldValidate = _this.shouldDoValidate(value, methods);
				if (shouldValidate) {
					var _error = _this.doValidate($(value), field, methods, itemObj);
					if (_error !== null) {
						error = _error;
						return false;
					}
				}
			});	
			return error;
		},
		doValidate: function($item, field, methods, itemObj) {
			var error = null;
			for (var i in methods) {
				if (i === 'validate') {
					continue;
				}

				if (i in defaultsMethod) {											
					var result = defaultsMethod[i].func(methods[i], $item, itemObj);
					console.log('doValidate defaultsMethod, result:',field,i,result);	
					console.log($item);				
					if (!result) {
						var errorText = this.getErrorText(field, i, methods[i]);	
						error= {
							elem: $item,
							param: methods[i],
							field: field,
							method: i,
							errorText: errorText
						};

						break;//每个字段只验证一个规则，有了错误不验证剩下的
					}
				} else if ((i in customMethod) && methods[i]) {//自定义规则要用只能是true,不能自定义参数
					//debugger;
					var result = customMethod[i].func($item);
					console.log('doValidate customMethod, result:',result);					
					if (!result) {
						var errorText = this.getErrorText(field, i, methods[i]);						
						error = {
							elem: $item,
							param: methods[i],
							field: field,
							method: i,
							errorText: errorText
						};

						break;//每个字段只验证一个规则，有了错误不验证剩下的
					}
				}
			}

			return error;
		},
		getMethodsByField: function(field) {
			if (this.rules[field]) {
				return this.rules[field];
			} else if (customIdRule[field]) {
				return customIdRule[field]['rules'];
			} else if (customClassRule[field]){					
				return customClassRule[field]['rules'];	
			} else {
				return null;
			}
		},
		getClassName: function(classList) {
			var classArr = $.trim(classList).split(/\s+/),i = 0, len = classArr.length;
			console.log('addClassRule:',classArr,customClassRule);
			for(; i < len; i++) {
				if (customClassRule['.'+classArr[i]]) {
					return '.'+classArr[i];
				}
			}
			return null;
		},
		getErrorText: function(field, method, value) {
			console.log('getErrorText:field'+field+',method:'+method);
			if (this.messages[field] && this.messages[field][method]) {
				//通过name方式的信息
				return this.messages[field][method];
			} else if (customClassRule[field] && customClassRule[field]['messages'][method]) {
				//classRule的信息
				return customClassRule[field]['messages'][method];
			}else if (customIdRule[field] && customIdRule[field]['messages'][method]) {
				//idRule的信息
				return customIdRule[field]['messages'][method];
			} else if (defaultsMethod[method] && defaultsMethod[method]['msg']) {
				//函数默认信息
				return defaultsMethod[method].msg.replace(/\[param\]/, this.valueToString(value));
			}
			return '';
		},
		valueToString: function(value) {
			if ($.isArray(value)) {
				return '['+value.toString()+']';
			}
			return value.toString();
		}
	};
	
	$.fn.XValidate = function(options) {
		return this.each(function(index, elem) {
			new XValidate($(elem), options);
		})
	}
}(jQuery));